#pragma once
#include"redisObject.hpp"
#include"stringObject.hpp"
#include"unordered_set.hpp"
#include<assert.h>
#define INTSET_ENC_INT16 2
#define INTSET_ENC_INT32 4
#define INTSET_ENC_INT64 8
namespace redisStruct
{
	struct intset
	{
		uint32_t _encoding;
		int32_t _length;
		int8_t _data[];
	public:
		static void* setSizePtr(void* ptr, int size, long long srcNum)
		{
			if (size == 2)
				*((int16_t*)ptr) = srcNum;
			else if (size == 4)
				*((int32_t*)ptr) = srcNum;
			else if (size == 8)
				*((int64_t*)ptr) = srcNum;
			else
				assert(false);
			return ptr;
		}
		static long long getSizePtr(void* ptr, int size)
		{
			if (size == 2)
				return *((int16_t*)ptr);
			else if (size == 4)
				return *((int32_t*)ptr);
			else if (size == 8)
				return *((int64_t*)ptr);
			else
				assert(false);
		}
		static void init(void*& pintset)
		{
			pintset  = malloc(sizeof(intset));
			((intset*)pintset)->_length = 0;
			((intset*)pintset)->_encoding = INTSET_ENC_INT16;
		}
		static void reCapacity(void*& pintset, int capacity, int size)
		{
			intset* pintset1 = (intset*)malloc(sizeof(intset) + capacity * size);
			for (int i = 0; i < ((intset*)pintset)->_length; i++)
			{
				setSizePtr(pintset1->_data + i * size, size, getSizePtr(((intset*)pintset)->_data + i * ((intset*)pintset)->_encoding, ((intset*)pintset)->_encoding));
			}
			pintset1->_encoding = size;
			pintset1->_length = ((intset*)pintset)->_length; //长度保持不变
			pintset = pintset1;
			pintset1 = nullptr;
		}
		static void insert(void*& pintset, long long num)
		{
			if (   ((intset*)pintset)->_encoding == INTSET_ENC_INT16 && num >= INT16_MIN && num <= INT16_MAX
				|| ((intset*)pintset)->_encoding == INTSET_ENC_INT32 && num >= INT32_MIN && num <= INT32_MAX  )
			{ //不需要整形提升
				reCapacity(pintset, ((intset*)pintset)->_length + 1, ((intset*)pintset)->_encoding);
			}
			else 
			{//需要整形提升
				int encod=0;
				if (num<INT16_MIN || num >INT16_MAX)
					encod = INTSET_ENC_INT32;
				if (num<INT32_MIN || num >INT32_MAX)
					encod = INTSET_ENC_INT64;

				reCapacity(pintset, ((intset*)pintset)->_length + 1, encod);
			} 

			//单趟插入排序
			int i;
			for (i = ((intset*)pintset)->_length - 1; i >= 0; i--)
			{
				if (num >= getSizePtr(((intset*)pintset)->_data + i * ((intset*)pintset)->_encoding, ((intset*)pintset)->_encoding))
					break;
				else
					memcpy(((intset*)pintset)->_data + (i + 1) * ((intset*)pintset)->_encoding,
						((intset*)pintset)->_data + i * ((intset*)pintset)->_encoding, 
						((intset*)pintset)->_encoding);
			}
			
			setSizePtr(((intset*)pintset)->_data + (i + 1) * ((intset*)pintset)->_encoding, ((intset*)pintset)->_encoding, num);
			((intset*)pintset)->_length++;
		}
		static void erase(void*& pintset, long long num)
		{
			int ret = find(pintset, num);

			if (ret == -1) return;
		    
			for (int i = ret + 1; i < ((intset*)pintset)->_length; i++)
			{
				memcpy( ((intset*)pintset)->_data + (i - 1) * ((intset*)pintset)->_encoding, 
					    ((intset*)pintset)->_data + i * ((intset*)pintset)->_encoding,
					    ((intset*)pintset)->_encoding );
			}
			((intset*)pintset)->_length--;
		}
		// -1不在里面 
		static int find(void*& pintset, long long nums)
		{
			if(((intset*)pintset)->_length == 0 ) return -1;
			//cout<<"find--"<<nums<<endl;
			intset* pints = (intset*)pintset;
			//二分查找
			char* left = (char*)pints->_data;
			char* right = (char*)left + (pints->_length - 1) * pints->_encoding;
			char* mid = nullptr;
			while (left <= right)
			{
				int gapNum = (right - left) / pints->_encoding;
				mid = left + (gapNum  / 2) * pints->_encoding;
				if (getSizePtr(mid, pints->_encoding) > nums)
				{
					right = mid - pints->_encoding;
				}
				else if (getSizePtr(mid, pints->_encoding) < nums)
				{
					left = mid + pints->_encoding;
				}
				else
					break;
			}

			if (left <= right)
			{
				return (mid - (char*)pints->_data) / pints->_encoding;
			}
			else
			{
				return -1;
			}
		}
		

	};

	class SetObject : public redisObject
	{
		
		
		void TransToHashTable(void*& _ptr)//已经是哈希编码则立刻返回
		{
			if (_encoding == TYPE_Ecoding_HASHTABLE) return; 
			if (_encoding != TYPE_Ecoding_INTSET) assert(false);

			unordered_set<stringObject*>* uset = new unordered_set<stringObject*>();

			char buf[30] = { 0 };
			for (int i = 0; i < ((intset*)_ptr)->_length; i++)
			{
				sprintf(buf, "%lld", intset::getSizePtr(((intset*)_ptr)->_data + i * ((intset*)_ptr)->_encoding, ((intset*)_ptr)->_encoding));
				stringObject* str1 = new stringObject(buf, strlen(buf));
				uset->insert(str1);
				str1->decrRefCount();
				memset(buf, 0, 30);
			}

			free(_ptr);
			_ptr = uset;
			_encoding = TYPE_Ecoding_HASHTABLE;
		}
		static bool IsCanTransToLongLong(const char* str, int size, long long& val)
		{
			
			
			char* endptr = nullptr;
			long long value = strtol(str, &endptr, 10);
			/*if(endptr==nullptr) cout<<"null"<<endl;
			else cout<<"not null"<<endl;*/
			if (*endptr != '\0') return false;
			char buf[30] = { 0 };
			snprintf(buf, 30, "%lld", value);
			if (strlen(buf) != size || memcmp(buf, str, size)) return false;
            cout<<"val"<<value<<endl;
			val = value;
			return true;

		}


	public:
		void* _ptr = nullptr;
		SetObject() :redisObject(TYPE_SET, TYPE_Ecoding_INTSET)
		{
			intset::init(_ptr);
		};
		~SetObject()
		{
			if(_encoding == TYPE_Ecoding_INTSET)
			free(_ptr);
			else if(_encoding == TYPE_Ecoding_HASHTABLE)
			delete((unordered_set<stringObject*>*)_ptr);
			else
			assert(false);
		}
		void add(stringObject* str)
		{
			if(find(str)) return;
			str->incrRefCount();
			
			bool IsLongLong=false;
			long long val=0;
			if (str->_encoding == TYPE_Encoding_INT||IsCanTransToLongLong(str->c_str(), str->_size, val))
			{  
				if (str->_encoding == TYPE_Encoding_INT)
					val = (long)str->c_str();
				if (_encoding == TYPE_Ecoding_INTSET)
					intset::insert(_ptr, val);
				
				else if (_encoding == TYPE_Ecoding_HASHTABLE)
					((unordered_set<stringObject*>*)_ptr)->insert(str);
				
			}
			else
			{
				TransToHashTable(_ptr);//已经是哈希编码则立刻返回
				((unordered_set<stringObject*>*)_ptr)->insert(str);
			}
		}
		void erase(stringObject* str1)
		{
			if (_encoding == TYPE_Ecoding_INTSET)
			{
				long long val=0;
				if (IsCanTransToLongLong(str1->_ptr, str1->_size, val) == false)
					return;
				else
					intset::erase(_ptr, val);
			}
			else if (_encoding == TYPE_Ecoding_HASHTABLE)
			{
				((unordered_set<stringObject*>*)_ptr)->erase(str1);
			}
		}

		bool find(stringObject* str1)
		{
			if (_encoding == TYPE_Ecoding_INTSET)
			{
				long long val = 0;
				if (str1->_encoding==TYPE_Encoding_SDS&&IsCanTransToLongLong(str1->_ptr, str1->_size, val) == false)
				{			
					return false;
				}
				else if(str1->_encoding==TYPE_Encoding_SDS)
					return intset::find(_ptr, val) == -1 ? false : true;
				else if(str1->_encoding==TYPE_Encoding_INT)
					return intset::find(_ptr, (long long)str1->c_str()) == -1 ? false : true;
                else
				    {
						assert(false);
						return true;
					}
				
			}
			else if (_encoding == TYPE_Ecoding_HASHTABLE)
			{			

			  return ((unordered_set<stringObject*>*)_ptr)->find(str1) == ((unordered_set<stringObject*>*)_ptr)->end() ? false : true;
			}
		}
		size_t size() const
		{
			if (_encoding == TYPE_Ecoding_INTSET)
				return ((intset*)_ptr)->_length;
			else if (_encoding == TYPE_Ecoding_HASHTABLE)
				return ((unordered_set<stringObject*>*)_ptr)->size();
			else
				assert(false);
		}
	};

	

	void test_intset()
	{
		SetObject set;
		stringObject* str1 = new stringObject("123", 3);
		stringObject* str2 = new stringObject("124", 3);
		set.add(str1);
		set.add(str2);
		stringObject* str3 = new stringObject("s", 1);
		set.add(str3);

		if (set.find(str1))
			cout << "exists" << endl;
		else
			cout << "not exists" << endl;
		if (set.find(str2))
			cout << "exists" << endl;
		else
			cout << "not exists" << endl;
		if (set.find(str3))
			cout << "exists" << endl;
		else
			cout << "not exists" << endl;
		if (set.find(new stringObject("111",3) ))
			cout << "exists" << endl;
		else
			cout << "not exists" << endl;
	}
}